package com.mark.conf;

import com.mark.security.KickoutSessionControlFilter;
import com.mark.security.ShiroRealm;
import com.mark.common.utils.PropertiesUtil;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @Description:
 * @Author: Kingsley
 * @CreateDate: 2019/1/1 19:58
 * @Version: 2.0
 * @Copyright : 豆浆油条个人非正式工作室
 */
@Configuration
public class ShiroConfig {

    /**
     * 读取redis数据库的配置信息
     */
    private  final String redisIp = PropertiesUtil.getDataSourceProperty("shiro.redis.ip");
    private  final int redisPort = Integer.parseInt(PropertiesUtil.getDataSourceProperty("shiro.redis.port","6379"));
    private  final int redisExpire = Integer.parseInt(PropertiesUtil.getDataSourceProperty("shiro.redis.expire","8000"));
    private  final int redisTimeout = Integer.parseInt(PropertiesUtil.getDataSourceProperty("shiro.redis.timeout","0"));
    /**
     * 同一个帐号最大会话数 默认1
     */
    private static final int LOGIN_MAX_SESSION = Integer.parseInt(PropertiesUtil.getProperty("login.maxSession","1")) ;
    /**
     * 踢出之前登录的/之后登录的用户 默认踢出之前登录的用户
     */
    private static final boolean LOGIN_KICKOUT_AFTER = Boolean.parseBoolean(PropertiesUtil.getProperty("login.kickoutAfter","false")) ;

    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //设置自定义的Realm
        securityManager.setRealm(shiroRealm());

        //设置缓存管理器,使用Redis作为第三方缓存
        securityManager.setCacheManager(cacheManager());

        //设置session回话管理器,RedisSessionDAO
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    @Bean
    public SessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(redisSessionDAO());
        return sessionManager;
    }

    /**
     * RedisSessionDAO shiro sessionDao层的实现 通过redis
     *
     * @return
     */
    @Bean
    public SessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        return redisSessionDAO;
    }

    /**
     * 使用Redis作为cacheManger(shiro-redis开源插件)
     *
     * @return ：
     */
    private CacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }

    /**
     * redis服务器的配置
     *
     * @return
     */
    private RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(redisIp);
        redisManager.setPort(redisPort);
        // 配置缓存过期时间
        redisManager.setExpire(redisExpire);
        redisManager.setTimeout(redisTimeout);
        return redisManager;
    }

    @Bean
    public Realm shiroRealm() {
        return new ShiroRealm();
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //自定义过滤器实现单点登录
        Map<String, Filter> filterMap = new LinkedHashMap<>();
        filterMap.put("kickout", kickoutSessionControlFilter());
        shiroFilterFactoryBean.setFilters(filterMap);

        //访问路径
        Map<String, String> filterChanDefinitionMap = new LinkedHashMap<>();
        //健康度检查放行的路径配置
        filterChanDefinitionMap.put("/actuator/**", "anon");
        //favicon标签获取的放行
        filterChanDefinitionMap.put("/favicon.ico", "anon");
        //Swagger2-UI的访问路径的放行
        filterChanDefinitionMap.put("/swagger-ui.html", "anon");
        filterChanDefinitionMap.put("/swagger/**", "anon");
        filterChanDefinitionMap.put("/webjars/**", "anon");
        filterChanDefinitionMap.put("/swagger-resources/**", "anon");
        filterChanDefinitionMap.put("/v2/**", "anon");
        filterChanDefinitionMap.put("/sys/user/registerLogin/logout.do", "logout");
        //Druid数据源访问路径的放行
        filterChanDefinitionMap.put("/druid/**", "anon");
        //登录和注册的控制器放行
        filterChanDefinitionMap.put("/sys/user/registerLogin/**", "anon");
        //其他请求都需要经过自定义的过滤器和权限验证
        filterChanDefinitionMap.put("/**", "authc,kickout");
        //用户需要登录的路径
        shiroFilterFactoryBean.setLoginUrl("/sys/user/registerLogin/unLogin.do");
        shiroFilterFactoryBean.setUnauthorizedUrl("/unanth");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChanDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 授权使用
     *
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }

    /**
     * 防止授权注解不起作用
     *
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor sourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        sourceAdvisor.setSecurityManager(securityManager);
        return sourceAdvisor;
    }

    /**
     * Shiro生命周期管理器
     *
     * @return
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /***
     * 限制同一账号同时登录人数控制
     * @return
     */
    @Bean
    public KickoutSessionControlFilter kickoutSessionControlFilter() {
        KickoutSessionControlFilter kickoutSessionControlFilter = new KickoutSessionControlFilter();
        kickoutSessionControlFilter.setCacheManager(cacheManager());
        kickoutSessionControlFilter.setSessionManager(sessionManager());
        kickoutSessionControlFilter.setKickoutAfter(LOGIN_KICKOUT_AFTER);
        kickoutSessionControlFilter.setMaxSession(LOGIN_MAX_SESSION);
        kickoutSessionControlFilter.setKickoutUrl("/auth/kickout");
        return kickoutSessionControlFilter;
    }

}
